package app

import (
	"bufio"
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"path/filepath"
	consts "qrsj-collect/backend/const"
	"qrsj-collect/backend/system"
	"qrsj-collect/backend/types"
	"qrsj-collect/backend/utils"
	"strconv"
	"strings"
	"sync"
	"time"
	"unsafe"

	"github.com/go-vgo/robotgo"
	jsoniter "github.com/json-iterator/go"
	"github.com/wailsapp/wails/v2/pkg/runtime"
)

type Task struct {
	mu         sync.Mutex
	cancelFunc context.CancelFunc
}

// App struct
type App struct {
	ctx           context.Context
	json          jsoniter.API
	addressInfo   types.AddressFile
	baseAddress   uintptr   // 人物坐标/钓鱼基址
	coordOffsets  []uintptr // 人物x坐标偏移
	fishOffsets   []uintptr // 钓鱼偏移
	angleOffsets  []uintptr // 角度偏移a, b需要-E8，即-232
	tempPath      string    // 临时路径
	currentHwnd   uintptr
	mu            sync.Mutex
	tasks         map[string]*Task
	coordInfo     CoordInfo // 人物坐标
	initCoordInfo CoordInfo // 初始坐标
	angleInfo     AngleInfo // 角度
}

type CoordInfo struct {
	X float32 `json:"x"`
	Y float32 `json:"y"`
	Z float32 `json:"z"`
}

type AngleInfo struct {
	A float32 `json:"a"`
	B float32 `json:"b"`
}

func NewMainApp() *App {
	return &App{
		tasks: make(map[string]*Task),
	}
}

func (a *App) Startup(ctx context.Context) {
	a.ctx = ctx
	a.json = jsoniter.ConfigCompatibleWithStandardLibrary
	// 配置初始化
	a.ReadConfigFile()
	// 执行定时任务
	a.IntervalReadAddressJob("read_address", 10)
}

func (a *App) Shutdown(ctx context.Context) {
	var conf types.ConfigApp
	if a.tempPath != "" {
		conf.LastOpenPath = a.tempPath
	}
	confJson, _ := a.json.MarshalIndent(conf, "", "  ")
	utils.WriteFile(filepath.Join(utils.GetConfigAppPath(), consts.CONFIG_FILE_NAME), confJson)
	a.StopTask("read_address")
}

// 通过类型和窗口名获取窗口句柄
func (a *App) GetWindowsHwnd(className string, windowName string) uintptr {
	hwnd, err := system.FindWindow(className, windowName)
	if err != nil {
		return 0
	}
	return uintptr(hwnd)
}

// 通过类型和窗口名获取进程ID
func (a *App) GetWindowsProcessID(className string, windowName string) int {
	return system.FindWindowProcessID(className, windowName)
}

// 获取所有窗口
func (a *App) GetAllWindows(className string, windowName string) map[string]interface{} {
	var windowsList []system.WindowInfo
	windowsList, _ = system.FindAllWindows(className, windowName)
	return map[string]interface{}{"code": http.StatusOK, "msg": "窗口获取成功", "data": windowsList}
}

// 绘制窗口矩形
func (a *App) HandleDrawWindowRectangle(hwnd uintptr, color uint32, duration int, interval int) {
	a.currentHwnd = hwnd
	// 先激活窗口
	// system.ActivateWindow(hwnd)
	time.Sleep(100 * time.Millisecond)
	if duration == 0 {
		duration = 500
	}
	if interval == 0 {
		interval = 20
	}
	durationTime := time.Duration(duration) * time.Millisecond
	intervalTime := time.Duration(interval) * time.Millisecond

	ticker := time.NewTicker(intervalTime)
	defer ticker.Stop()
	timer := time.NewTimer(durationTime)
	defer timer.Stop()

	var wg sync.WaitGroup
	wg.Add(1)

	go func() {
		defer wg.Done()
		for {
			select {
			case <-ticker.C:
				system.DrawWindowRectangle(hwnd, 3, color, true)
			case <-timer.C:
				return
			}
		}
	}()
	wg.Wait()
}

// 获取基址
func (a *App) GetBaseAddress(hwnd uintptr) {
	var relative uintptr
	relativeUint, _ := strconv.ParseUint(a.addressInfo.CoordInfo.Relative, 16, 64)
	if a.addressInfo.CoordInfo.IsAdd {
		relative = uintptr(relativeUint)
	} else {
		relative = ^uintptr(relativeUint) + 1
	}
	baseAddress, err := system.GetBaseAddress(hwnd, a.coordOffsets, a.addressInfo.CoordInfo.PatternBase, relative)
	fmt.Printf("baseAddress: %x\n", baseAddress)
	if err != nil {
		log.Printf("基址获取失败\nerr=%v", err)
		return
	}
	if baseAddress == 0 {
		log.Printf("未找到基址")
		return
	}
	a.baseAddress = baseAddress
	a.addressInfo.CoordInfo.BaseAddressStr = fmt.Sprintf("%x", baseAddress)
	a.addressInfo.FishInfo.BaseAddressStr = fmt.Sprintf("%x", baseAddress)
	a.addressInfo.AngleInfo.BaseAddressStr = fmt.Sprintf("%x", baseAddress)
	utils.UpdateAddressJSON(a.json, &a.addressInfo)
}

// 读取配置文件
func (a *App) ReadConfigFile() {
	configAppPath := utils.InitConfigPath()
	fullFilePath := filepath.Join(configAppPath, consts.CONFIG_FILE_NAME)
	exists := utils.FileExists(fullFilePath)
	conf := types.ConfigApp{}
	if !exists {
		jsonData, _ := a.json.MarshalIndent(conf, "", "  ")
		err := utils.WriteFile(fullFilePath, jsonData)
		if err != nil {
			log.Printf("首次写入配置文件失败\nerr=%v", err)
			return
		}
	} else {
		bytesData, _ := utils.ReadFile(fullFilePath)
		err := a.json.Unmarshal(bytesData, &conf)
		if err != nil {
			log.Printf("读取配置文件失败\nerr=%v", err)
			return
		}
		a.tempPath = conf.LastOpenPath
	}
}

// 定时读取地址文件
func (a *App) IntervalReadAddressJob(taskID string, interval time.Duration) {
	a.mu.Lock()
	ctx, cancel := context.WithCancel(context.Background())
	task := &Task{
		cancelFunc: cancel,
	}
	a.tasks[taskID] = task
	fmt.Printf("任务{%v}已启动\n", taskID)
	a.mu.Unlock()

	executeTask := func() {
		utils.ReadAddressJSON(a.json, &a.addressInfo)
		// 偏移转换
		var offsets1 []uintptr
		for _, offset := range a.addressInfo.CoordInfo.OffsetsStr {
			offsetUint, _ := strconv.ParseUint(offset, 16, 64)
			offsets1 = append(offsets1, uintptr(offsetUint))
		}
		a.coordOffsets = offsets1
		var offsets2 []uintptr
		for _, offset := range a.addressInfo.FishInfo.OffsetsStr {
			offsetUint, _ := strconv.ParseUint(offset, 16, 64)
			offsets2 = append(offsets2, uintptr(offsetUint))
		}
		a.fishOffsets = offsets2
		var offsets3 []uintptr
		for _, offset := range a.addressInfo.AngleInfo.OffsetsStr {
			offsetUint, _ := strconv.ParseUint(offset, 16, 64)
			offsets3 = append(offsets3, uintptr(offsetUint))
		}
		a.angleOffsets = offsets3
		// 基址转换
		value, _ := strconv.ParseUint(a.addressInfo.CoordInfo.BaseAddressStr, 16, 64)
		a.baseAddress = uintptr(value)
	}

	go func() {
		executeTask()
		ticker := time.NewTicker(interval * time.Second)
		defer ticker.Stop()
		for {
			select {
			case <-ticker.C:
				executeTask()
			case <-ctx.Done():
				fmt.Printf("任务{%v}已停止", taskID)
				return
			}
		}
	}()
}

func (a *App) StopTask(taskID string) {
	a.mu.Lock()
	defer a.mu.Unlock()
	if task, exists := a.tasks[taskID]; exists {
		task.mu.Lock()
		if task.cancelFunc != nil {
			task.cancelFunc()
			task.cancelFunc = nil
		}
		task.mu.Unlock()
		delete(a.tasks, taskID)
		fmt.Printf("任务{%v}已停止\n", taskID)
	} else {
		fmt.Printf("任务{%v}不存在\n", taskID)
	}
}

// 获取文本列表
func (a *App) GetCollectScriptList() map[string]interface{} {
	dir, _ := os.Getwd()
	folder := filepath.Join(dir, "script")
	// 获取当前目录下的所有文件
	files := make([]map[string]string, 0)
	err := filepath.Walk(folder, func(path string, info os.FileInfo, err error) error {
		if info == nil {
			return nil
		}
		if info.IsDir() {
			return nil
		}
		if filepath.Ext(info.Name()) == ".txt" || filepath.Ext(info.Name()) == ".ini" {
			// fileName := info.Name()[:len(info.Name())-4]
			// fileName := strings.TrimSuffix(info.Name(), ".txt")
			fileName := info.Name()
			files = append(files, map[string]string{"key": path, "title": fileName, "description": fileName})
		}
		return nil
	})
	if err != nil {
		return map[string]interface{}{"code": http.StatusInternalServerError, "msg": "获取文件列表失败", "data": nil}
	}
	return map[string]interface{}{"code": http.StatusOK, "msg": "获取文件列表成功", "data": files}
}

// 读取人物坐标
func (a *App) ReadCharacterCoord() map[string]interface{} {
	return map[string]interface{}{"code": http.StatusOK, "msg": nil, "data": a.coordInfo}
}

// 运行脚本
func (a *App) RunningScript(path, taskID string, num int) {
	for i := 1; i < 4; i++ {
		runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("倒计时%d", 4-i))
		time.Sleep(1 * time.Second)
	}
	runtime.EventsEmit(a.ctx, "setLog", "任务已启动")
	fmt.Printf("path: %s, taskID: %s\n", path, taskID)
	a.mu.Lock()
	defer a.mu.Unlock()
	ctx, cancel := context.WithCancel(context.Background())
	task := &Task{
		cancelFunc: cancel,
	}
	a.tasks[taskID] = task

	go func() {
		var i = 0
		for {
			select {
			case <-ctx.Done():
				runtime.EventsEmit(a.ctx, "setLog", "任务已停止")
				return
			default:
				a.executeTask(path, taskID)
				i++
				if num > 0 && i > num {
					runtime.EventsEmit(a.ctx, "setLog", "任务已完成")
					runtime.EventsEmit(a.ctx, "onScript", false)
					a.StopTask(taskID)
					return
				}
				if _, exists := a.tasks[taskID]; exists {
					if num == 0 {
						runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("任务已完成%d次", i))
					} else {
						runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("任务已完成%d / %d次", i, num))
					}

				}
				time.Sleep(1 * time.Second)
			}
		}
	}()

	go func() {
		address, err := system.ReadMultiLevelPointerAddress(a.currentHwnd, a.baseAddress, a.coordOffsets, true)
		if err != nil {
			runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("读取坐标失败: %v", err))
			runtime.EventsEmit(a.ctx, "onScript", false)
		}
		addressAngle, err := system.ReadMultiLevelPointerAddress(a.currentHwnd, a.baseAddress, a.angleOffsets, true)
		if err != nil {
			runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("读取角度失败: %v", err))
			runtime.EventsEmit(a.ctx, "onScript", false)
		}
		isFirst := true
		for {
			select {
			case <-time.After(30 * time.Millisecond):
				xPtr, err := system.ReadMemoryValues(a.currentHwnd, address)
				if err != nil {
					runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("读取坐标失败: %v", err))
					// runtime.EventsEmit(a.ctx, "onScript", false)
					time.Sleep(2 * time.Second)
				}
				yPtr, err := system.ReadMemoryValues(a.currentHwnd, address+8)
				if err != nil {
					runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("读取坐标失败: %v", err))
					// runtime.EventsEmit(a.ctx, "onScript", false)
					time.Sleep(2 * time.Second)
				}
				a.coordInfo.X = *(*float32)(unsafe.Pointer(&xPtr))
				a.coordInfo.Y = *(*float32)(unsafe.Pointer(&yPtr))
				if isFirst {
					a.initCoordInfo.X = a.coordInfo.X
					a.initCoordInfo.Y = a.coordInfo.Y
					isFirst = false
				}
				aPtr, err := system.ReadMemoryValues(a.currentHwnd, addressAngle)
				if err != nil {
					runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("读取角度a失败: %v", err))
					// runtime.EventsEmit(a.ctx, "onScript", false)
					time.Sleep(2 * time.Second)
				}
				bPtr, err := system.ReadMemoryValues(a.currentHwnd, addressAngle-232)
				if err != nil {
					runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("读取角度b失败: %v", err))
					// runtime.EventsEmit(a.ctx, "onScript", false)
					time.Sleep(2 * time.Second)
				}
				a.angleInfo.A = *(*float32)(unsafe.Pointer(&aPtr))
				a.angleInfo.B = *(*float32)(unsafe.Pointer(&bPtr))
			case <-ctx.Done():
				return
			}
		}
	}()
}

// 脚本任务
func (a *App) executeTask(path, taskID string) {
	file, err := os.Open(path)
	if err != nil {
		runtime.EventsEmit(a.ctx, "setLog", "文件打开失败"+err.Error())
		runtime.EventsEmit(a.ctx, "onScript", false)
		return
	}
	defer file.Close()
	var commands = a.commandsFunc()
	scanner := bufio.NewScanner(file)
	var lineNum = 0
	for scanner.Scan() {
		if _, exists := a.tasks[taskID]; !exists {
			return
		}
		lineNum++
		line := scanner.Text()
		if strings.Contains(line, "//") {
			ann := strings.Split(line, "//")[0]
			ann = strings.TrimSpace(ann)
			if ann == "" {
				continue
			}
		}
		parts := strings.Split(line, "|")
		if len(parts) == 0 {
			continue
		}
		for _, part := range parts {
			part = strings.TrimSpace(part)
			// 判断是否存在注释
			if strings.Contains(part, "//") {
				part = strings.Split(part, "//")[0]
				part = strings.TrimSpace(part)
				if part == "" {
					continue
				}
			}
			runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("->%s", part))
			// 解析指令和参数
			fields := strings.Fields(part)
			if len(fields) > 0 {
				cmd := fields[0]
				var args []string
				if len(fields) > 1 {
					args = strings.Split(fields[1], ",")
				}
				if fn, ok := commands[cmd]; ok {
					fn(taskID, args...)
				} else {
					runtime.EventsEmit(a.ctx, "setLog", fmt.Sprintf("第%d行, 未找到指令: %s", lineNum, part))
				}
			}
		}
		time.Sleep(500 * time.Millisecond)
	}
}

// 获取x和y坐标
func (a *App) GetXYCoord() (x, y float32, err error) {
	address, err := system.ReadMultiLevelPointerAddress(a.currentHwnd, a.baseAddress, a.coordOffsets, true)
	if err != nil {
		return
	}
	xPtr, err := system.ReadMemoryValues(a.currentHwnd, address)
	if err != nil {
		return
	}
	yPtr, err := system.ReadMemoryValues(a.currentHwnd, address+8)
	if err != nil {
		return
	}
	x = *(*float32)(unsafe.Pointer(&xPtr))
	y = *(*float32)(unsafe.Pointer(&yPtr))
	return
}

// 多点坐标采集
func (a *App) SetMultiCut() {
	var initStr string
	readStr, _ := robotgo.ReadAll()
	if strings.Contains(readStr, "移动 ") && strings.Contains(readStr, "|") {
		initStr = readStr
	}
	if (a.currentHwnd == 0) || (a.baseAddress == 0) {
		runtime.EventsEmit(a.ctx, "errorMsg", "窗口或基址不存在")
		return
	}
	x, y, err := a.GetXYCoord()
	if err != nil {
		runtime.EventsEmit(a.ctx, "errorMsg", fmt.Sprintf("读取坐标失败: %v", err))
		return
	}

	str := fmt.Sprintf("移动 %.2f,%.2f|", x, y)
	robotgo.WriteAll(initStr + str)
}

// 单点坐标采集
func (a *App) SetSingleCut() {
	if (a.currentHwnd == 0) || (a.baseAddress == 0) {
		runtime.EventsEmit(a.ctx, "errorMsg", "窗口或基址不存在")
		return
	}
	x, y, err := a.GetXYCoord()
	if err != nil {
		runtime.EventsEmit(a.ctx, "errorMsg", fmt.Sprintf("读取坐标失败: %v", err))
		return
	}

	str := fmt.Sprintf("移动 %.2f,%.2f", x, y)
	robotgo.WriteAll(str)
}

// 鼠标采集
func (a *App) SetMouseCut() {
	// 获取当前鼠标位置
	mouseX, mouseY := robotgo.Location()
	// 获取缩放比例
	f := robotgo.ScaleF(-1)
	mouseX = int(float64(mouseX) / f)
	mouseY = int(float64(mouseY) / f)
	str := fmt.Sprintf("鼠标移动 %d,%d", mouseX, mouseY)
	robotgo.WriteAll(str)
}
